00:00:00
Conditional 注解
一、Spring Boot @Conditional 注解
1.1 解决的问题
在一个智能的、自动配置的框架中,我们往往需要有条件地创建 Bean。例如:
- 只有当类路径下存在某个类时,才创建某个 Bean。
- 只有当配置文件中设置了某个特定属性时,才启用某个配置。
- 只有当系统中没有其他同类型的 Bean 时,才创建默认的 Bean。
@Conditional 注解就是为了实现这种"条件化配置"而生的。它是 Spring Boot 自动配置的决策引擎。
1.2 核心
@Conditional 是 Spring Framework 4.0 引入的核心注解,它本身并不直接提供条件判断逻辑,而是接收一个或多个实现了 Condition 接口的类。
java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}Condition 接口只有一个方法:
java
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}matches方法返回true,则条件成立,Bean 会被创建。matches方法返回false,则条件不成立,Bean 不会被创建。ConditionContext: 提供访问环境、类加载器、BeanFactory 等信息的上下文。AnnotatedTypeMetadata: 提供访问被@Conditional注解的元素的元数据。
简单示例: 假设我们只想在 Window 操作系统上创建一个 Bean。
- 创建自定义 Condition:
java
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 从环境信息中获取操作系统名称
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.toLowerCase().contains("windows");
}
}- 使用 @Conditional 注解:
java
@Configuration
public class MyConfig {
@Bean
// 只有 Windows 系统下,此 Bean 才会被创建
@Conditional(WindowsCondition.class)
public MyService windowsService() {
return new WindowsService();
}
}二、@Conditional 衍生注解
1 @ConditionalOnBean 与 @ConditionalOnMissingBean
- 根据 Spring 容器中是否存在某个 Bean 来决定配置。
java
@Bean
// 关键!只有当容器中没有 DataSource 类型的 Bean 时,才创建这个默认的 Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.build();
}这个注解是实现 "默认配置" 和 "用户自定义配置覆盖" 的核心。
2 @ConditionalOnClass 与 @ConditionalOnMissingClass
- 根据类路径下是否存在某个类来决定配置。
java
@Configuration
// 当类路径下存在 DataSource 类时,该配置类才生效
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
// ...
}3 @ConditionalOnWebApplication 与 @ConditionalOnNotWebApplication
- 根据当前应用是否是 Web 应用来决定配置。
java
@Configuration
// 只有在 Web 应用中,这个自动配置才生效
@ConditionalOnWebApplication
public class WebMvcAutoConfiguration {
// ...
}4 @ConditionalOnWarDeployment 与 @ConditionalOnNotWarDeployment
- 该注解仅在应用以传统WAR包方式部署时生效(即部署到Web服务器或应用服务器中),若应用使用嵌入式服务器(如内嵌的Tomcat),则条件不匹配,注解修饰的配置不会生效。
java
@Bean
@ConditionalOnWarDeployment
public WarWebService warWebService() {
return new WarWebService();
}5 @ConditionalOnProperty
- 根据配置文件中的属性来决定配置。
java
@Bean
@ConditionalOnProperty(prefix = "app.feature", name = "cache.enabled", havingValue = "true")
public CacheService cacheService() {
return new CacheService();
}只有当 app.feature.cache.enabled=true 时,才会创建 CacheService。
6 @ConditionalOnExpression
- 使用 SpEL 表达式进行复杂条件判断。
java
@Bean
@ConditionalOnExpression("'${app.mode}'.equals('cluster') && ${app.nodes} > 1")
public ClusterService clusterService() {
return new ClusterService();
}7 @ConditionalOnResource
- 只有当类路径下存在指定的资源文件时,配置才生效。
java
@Bean
@ConditionalOnResource(resources = "classpath:config/my-config.xml")
public MyService myService() {
return new MyService();
}8 @ConditionalOnJava
- 指定某种java版本或者版本之前才能生效
java
@Bean
@ConditionalOnJava(range = "OLDER_THAN",value="TWENTY_ONE")
public MyService myService() {
return new MyService();
}只有当前运行的 java 版本小于 21 配置才生效
9 @ConditionalOnJndi
- 依赖 JNDI 资源:只有当类路径中存在指定的 JNDI 资源(如 java:comp/env/jdbc/DataSource),目标
Bean或配置类才会被加载。 - 适用场景:常用于需要依赖 JNDI 数据源的配置,例如 JNDI 数据库连接池的自动配置。
java
@Configuration
@ConditionalOnJndi("java:comp/env/jdbc/DataSource")
public class JndiDataConfig {
// 配置类定义
}10 @ConditionalOnSingleCandidate
- 唯一性校验:当容器中存在且仅存在一个指定类型的
Bean时,条件成立,触发Bean或配置类的注册。 - 适用场景:
- 依赖唯一
Bean:确保某个Bean的依赖项在容器中唯一(如Primary Bean) - 可插拔实现:在多个实现类中选择唯一实现(需配合
@Primary注解)
- 依赖唯一
java
@Configuration
public class AnotherConfiguration {
@Bean
@ConditionalOnSingleCandidate(MyService.class)
public AnotherBean anotherBean(MyService myService) {
return new AnotherBean(myService);
}
}11 @ConditionalOnThreading
- 线程模型适配:支持
JDK 21的虚拟线程(Project Loom),通过配置Spring Boot的线程策略(如spring.threads.virtual.enabled=true)决定且处于active状态是否加载特定组件。 - 条件化配置:避免不同线程模型下的兼容性问题,例如针对虚拟线程优化异步处理逻辑。
java
@Configuration
public class MyThreadConfig {
@Bean
@ConditionalOnThreading(ThreadingType.VIRTUAL)
public MyVirtualThreadAwareBean virtualThreadBean() {
return new MyVirtualThreadAwareBean();
}
@Bean
@ConditionalOnThreading(ThreadingType.PLATFORM)
public MyPlatformThreadAwareBean platformThreadBean() {
return new MyPlatformThreadAwareBean();
}
}12 @ConditionalOnCheckpointRestore
- 存在类
org.crac.Resource条件时判定成立
java
@Configuration
public class MyThreadConfig {
@Bean
@ConditionalOnCheckpointRestore
public CheckpointRestore virtualThreadBean() {
// ...
}
}13 @ConditionalOnCloudPlatform
- spring 配置文件中指定了云平台时条件成立,例如:
spring.main.cloud-platform=AZURE_APP_SERVICE
java
@Configuration
public class MyThreadConfig {
@Bean
@ConditionalOnCloudPlatform("AZURE_APP_SERVICE")
public AZURCloudPlateformService virtualThreadBean() {
// ...
}
}三、总结
| 条件注解 | 判断条件 |
|---|---|
| ConditionalOnClass | 类加载器中存在某个类 |
| ConditionalOnMissingClass | 类加载器中不存在指定类 |
| ConditionalOnBean | Spring 容器中存在某个指定 Bean |
| ConditionalOnMissingBean | Spring 容器中不存在某个指定 Bean |
| ConditionalOnSingleCandidate | Spring 容器中是否存在且只存在一个对应的实例,或虽然有多个但是指定首选的 Bean 生效 |
| ConditionalOnJava | 指定Java版本符合要求生效 |
| ConditionalOnJndi | 存在JNDI |
| ConditionalOnCloudPlatform | 云平台,支持:CLOUD_FOUNDRY、HEROKU、SAP、NOMAD、KUBERNETES、AZURE_APP_SERVICE |
| ConditionalOnCheckpointRestore | 存在类 orc.crac.Resource |
| ConditionalOnWebApplication | Web应用生效 |
| ConditionalOnNotWebApplication | 不是Web应用生效 |
| ConditionalOnWarDeployment | War应用生效 |
| ConditionalOnNotWarDeployment | 不是War应用生效 |
| ConditionalOnResource | 当指定资源文件出现则生效 |
| ConditionalOnProperty | 应用环境中的属性满足条件生效 |
| ConditionalOnExpression | 判断SpEL表达式成立生效 |
| ConditionalOnThreading | 指定线程处于active状态 |